package com.andy.infrastructure.aop;


import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public abstract class AbstractAopEnhancer implements AopEnhancer {

    /**
     * 实现增强功能，封装使用增强的方式，暴露钩子方法让子类去实现
     * @param target
     * @return
     * @throws Exception
     */
    @Override
    public Object enhance(Object target) throws Exception {
        //获取增强方式
        String enhanceType = getEnhanceType(target);
        //增强对象或直接返回
        if("JDK".equals(enhanceType)){
            //选用jdk动态代理
            return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(),
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            return doEnhance(target, method, args);
                        }
                    });
        }else if("CGLIB".equals(enhanceType)){
            //选用cglib
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(target.getClass());
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                    return doEnhance(target,method,args);
                }
            });
            return enhancer.create();
        }else{
            return target;
        }
    }

    /**
     * 获取当前对应增强的方式（JDK,CGLIB,不增强）
     * @param target
     * @return
     * @throws Exception
     */
    private String getEnhanceType(Object target) throws Exception {
        //如果target实现了接口，并且接口上有transaction注解，则使用jdk代理
        if(target.getClass().getInterfaces().length>0){
            Class interfaceClz = target.getClass().getInterfaces()[0];
            Class clz = target.getClass();
            if(hasAnno(interfaceClz)) return "JDK";
            if(hasAnno(clz)) return "CGLIB";
        }else{
            //若没有实现接口，但是存在事物注解，则返回cgLib
            if(hasAnno(target.getClass())) return "CGLIB";
        }
        return null;
    }

    /**
     * 这个类上是否有方法拥有注解
     * @param clz
     * @return
     */
    private boolean hasAnno(Class clz){
        Method[] methods = clz.getMethods();
        for (Method method : methods) {
            if(method.getAnnotation(getAnnotationClass())!=null){
                return true;
            }
        }
        return false;
    }

    /**
     * 执行增强的逻辑
     * @param target
     * @param method
     * @param args
     * @return
     * @throws Exception
     */
    abstract Object doEnhance(Object target,Method method,Object[] args) throws Exception ;

    /**
     * 获取这个增强对应的注解
     * @return
     */
    abstract Class getAnnotationClass();
}
